package org.ewancle.springbootelasticsearch.config;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.kerberos.client.KerberosRestTemplate;

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;

// https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/migrate-hlrc.html
// https://juejin.cn/post/7346148341384282151#heading-1
@Configuration
public class ElasticsearchConfig {

    @Value("${elasticsearch.host}")
    private String host;

    @Value("${elasticsearch.port}")
    private int port;

    @Value("${elasticsearch.scheme:https}")
    private String scheme;

    @Value("${kerberos.service-principal}")
    private String servicePrincipal;

    @Value("${kerberos.keytab-location}")
    private String keytabLocation;

    /*@Bean
    public RestHighLevelClient restHighLevelClient() {
        RestClientBuilder builder = RestClient.builder(
                        new HttpHost(host, port, scheme))
                .setHttpClientConfigCallback(this::configureKerberos);

        return new RestHighLevelClient(builder);
    }

    @Bean
    public ElasticsearchClient elasticsearchClient(RestHighLevelClient restHighLevelClient) {
        // 使用现有的RestClient来创建ElasticsearchClient (8.x)
        RestClient restClient = restHighLevelClient.getLowLevelClient();
        ElasticsearchTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());

        return new ElasticsearchClient(transport);
    }*/
    @Bean
    public ElasticsearchClient elasticsearchClient() {
        // 使用现有的RestClient来创建ElasticsearchClient (8.x)
        // 主机列表
        HttpHost[] httpHosts = new HttpHost[1];
        httpHosts[0] = new HttpHost(host, port, scheme);
        RestClient restClient;

        if("https".equalsIgnoreCase(scheme)){
            final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(AuthScope.ANY,new UsernamePasswordCredentials("elastic", "changeme"));
            restClient = RestClient.builder(httpHosts)
                    .setCompressionEnabled(true)
                    .setHttpClientConfigCallback(httpClientBuilder -> {
                        // 这里设置鉴权需要的用户名elastic和对应密码
                        httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                        SSLContextBuilder sslContextBuilder = SSLContexts.custom();
                        try {
                            sslContextBuilder.loadTrustMaterial(new TrustStrategy() {
                                @Override
                                public boolean isTrusted(X509Certificate[] chain, String authType) {
                                    // 在这里跳过证书信息校验
                                    // System.out.println("isTrusted|" + authType + "|" + Arrays.toString(chain));
                                    return true;
                                }
                            });
                        } catch (NoSuchAlgorithmException | KeyStoreException e) {
                            //e.printStackTrace();
                            throw new RuntimeException(e);
                        }
                        try {
                            httpClientBuilder.setSSLContext(sslContextBuilder.build());
                        } catch (KeyManagementException | NoSuchAlgorithmException e) {
                            throw new RuntimeException(e);
                        }
                        // 这里跳过主机名称校验
                        httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
                        return httpClientBuilder;
                    }).build();
        }else { // kerberos认证
            restClient = RestClient.builder(httpHosts)
                    .setCompressionEnabled(true)
                    .setHttpClientConfigCallback(this::configureKerberos)
                    .build();
        }

        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        return new ElasticsearchClient(transport);
    }

    @Bean
    public KerberosRestTemplate kerberosRestTemplate() {
        return new KerberosRestTemplate(keytabLocation, servicePrincipal);
    }

    private HttpAsyncClientBuilder configureKerberos(HttpAsyncClientBuilder httpClientBuilder) {
        Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
                .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(true))
                .build();
        // 忽略证书
        SSLContextBuilder sslContextBuilder = SSLContexts.custom();
        try {
            sslContextBuilder.loadTrustMaterial((chain, authType) -> {
                // 在这里跳过证书信息校验
                //System.out.println("isTrusted|" + authType + "|" + Arrays.toString(chain));
                return true;
            });
        } catch (NoSuchAlgorithmException | KeyStoreException e) {
            throw new RuntimeException(e);
        }
        try {
            httpClientBuilder.setSSLContext(sslContextBuilder.build());
        } catch (KeyManagementException | NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        // 这里跳过主机名称校验
        httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);

        return httpClientBuilder.setDefaultAuthSchemeRegistry(authSchemeRegistry);
    }
}
